﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using FGrubert.VpcManager.Objects.MachineComparer;

namespace FGrubert.VpcManager
{
    /// <summary>
    /// Main form of the scanner
    /// </summary>
    public partial class FrmMain : Form
    {
        /// <summary>
        /// The current scanner instance
        /// </summary>
        private FolderScanner scanner;

        public FrmMain()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Starts the scan
        /// </summary>
        private void PerformScan()
        {
            lstMachines.Items.Clear();

            bgWorker.RunWorkerAsync();
        }


        /// <summary>
        /// Method raised when a new machine was detected
        /// </summary>
        /// <param name="machine">The new machine that was detected</param>
        private void scanner_NewMachine(Machine machine)
        {
            if (lstMachines.InvokeRequired)
            {
                DlgMachineInformationChanged dlgNewMachine = new DlgMachineInformationChanged(scanner_NewMachine);
                lstMachines.Invoke(dlgNewMachine, machine);
            }
            else
            {
                ListViewItem item = lstMachines.Items.Add(machine.Name);
                item.SubItems.Add(machine.Filename);
                item.SubItems.Add(machine.Nics.Count.ToString());
                item.SubItems.Add(string.Format(FrmMainLocals.RamText, machine.RamSize));
                item.SubItems.Add(machine.State.ToString());
                item.Tag = machine;

                machine.Changed += new DlgMachineInformationChanged(machine_Changed);

                UpdateListHighlighting();
            }
        }

        /// <summary>
        /// Raised when a new folder is scanned
        /// </summary>
        /// <param name="folderName">The name of the scanned folder</param>
        private void scanner_ScanningFolder(string folderName)
        {
            if (statusStrip.InvokeRequired)
            {
                DlgScanningFolder dlgScanningFolder = new DlgScanningFolder(scanner_ScanningFolder);
                statusStrip.Invoke(dlgScanningFolder, folderName);
            }
            else
            {
                toolStatusLabel.Text = string.Format(FrmMainLocals.ScanningText, folderName);
            }
        }

        /// <summary>
        /// Updates the highlighting of all machines in the list
        /// </summary>
        private void UpdateListHighlighting()
        {
            List<Machine> machines = new List<Machine>(lstMachines.Items.Count);
            foreach (ListViewItem currentItem in lstMachines.Items)
            {
                machines.Add((Machine)currentItem.Tag);
            }

            foreach (ListViewItem item in lstMachines.Items)
            {
                Machine machine = (Machine)item.Tag;

                bool doubleMac = false;
                foreach (Nic nic in machine.Nics)
                {
                    doubleMac = machines.Exists(m => m.Filename != machine.Filename && m.Nics.Exists(n => !string.IsNullOrEmpty(n.Mac) && n.Mac == nic.Mac));
                    if (doubleMac)
                    {
                        break;
                    }
                }

                if (doubleMac)
                {
                    item.BackColor = Color.Red;
                }
                else
                {
                    item.BackColor = SystemColors.Window;
                }
            }
        }

        /// <summary>
        /// The information of a machine was changed
        /// </summary>
        /// <param name="machine">The machine that was changed</param>
        public void machine_Changed(Machine machine)
        {
            if (lstMachines.InvokeRequired)
            {
                DlgMachineInformationChanged dlgMachineChanged = new DlgMachineInformationChanged(machine_Changed);
                lstMachines.Invoke(dlgMachineChanged, machine);
            }
            else
            {
                foreach (ListViewItem item in lstMachines.Items)
                {
                    if (item.Tag == machine)
                    {
                        item.SubItems[0].Text = machine.Name;
                        item.SubItems[1].Text = machine.Filename;
                        item.SubItems[2].Text = machine.Nics.Count.ToString();
                        item.SubItems[3].Text = string.Format(FrmMainLocals.RamText, machine.RamSize);
                        item.SubItems[4].Text = machine.State.ToString();
                    }
                }

                if (machine == CurrentMachine())
                {
                    UpdateMachineDetails(machine);
                }

                UpdateListHighlighting();
            }
        }

        /// <summary>
        /// Update the details view
        /// </summary>
        private void lstMachines_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lstMachines.SelectedItems.Count > 0)
            {
                Machine machine = (Machine)lstMachines.SelectedItems[0].Tag;
                UpdateMachineDetails(machine);
            }
        }

        /// <summary>
        /// Updates the details of the given machine
        /// </summary>
        /// <param name="machine">The machine to show the details for</param>
        private void UpdateMachineDetails(Machine machine)
        {
            lblDetailName.Text = machine.Name;
            lblDetailFile.Text = machine.Filename;
            lblDetailMemory.Text = string.Format(FrmMainLocals.RamText, machine.RamSize);
            pnlDetailNics.Controls.Clear();
            pnlDetailNics.Height = machine.Nics.Count * 26;
            for (int i = 0; i < machine.Nics.Count; i++)
            {
                Label myLabel1 = new Label();
                myLabel1.Top = i * 26;
                myLabel1.Height = 13;
                myLabel1.Width = pnlDetailNics.Width;
                myLabel1.AutoEllipsis = true;
                myLabel1.Text = string.Format(FrmMainLocals.NicDetail1Text, machine.Nics[i].Id, machine.Nics[i].Name, machine.Nics[i].Mac);
                myLabel1.Parent = pnlDetailNics;

                Label myLabel2 = new Label();
                myLabel2.Top = (i * 26) + 13;
                myLabel2.Left = 15;
                myLabel2.Height = 13;
                myLabel2.Width = pnlDetailNics.Width - myLabel2.Left;
                myLabel2.AutoEllipsis = true;
                myLabel2.Text = string.Format(FrmMainLocals.NicDetail2Text, machine.Nics[i].Id, machine.Nics[i].Name, machine.Nics[i].Mac);
                myLabel2.Parent = pnlDetailNics;
            }
        }

        /// <summary>
        /// Starts the current machine
        /// </summary>
        private void btnStartMachine_Click(object sender, EventArgs e)
        {
            StartMachine();
        }

        /// <summary>
        /// Returns the current machine
        /// </summary>
        /// <returns>The current machine or null</returns>
        private Machine CurrentMachine()
        {
            if (lstMachines.SelectedItems.Count > 0)
            {
                Machine machine = (Machine)lstMachines.SelectedItems[0].Tag;
                return machine;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Starts the current machine
        /// </summary>
        private void StartMachine()
        {
            try
            {
                Machine machine = CurrentMachine();
                if (machine != null)
                {
                    machine.StartMachine();
                }
            }
            catch (System.IO.FileNotFoundException)
            {
                MessageBox.Show(this, FrmMainLocals.MsgMachineNotFound, FrmMainLocals.MsgMachineNotFoundCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }            
        }

        /// <summary>
        /// Starts the current machine
        /// </summary>
        private void lstMachines_DoubleClick(object sender, EventArgs e)
        {
            StartMachine();
        }

        /// <summary>
        /// Set the menu items according to the current machine
        /// </summary>
        private void ctxMnuMachine_Opening(object sender, CancelEventArgs e)
        {
            mnuItemResetNicMac.DropDownItems.Clear();
            Machine machine = CurrentMachine();
            if (machine == null)
            {
                return;
            }
            foreach (Nic nic in machine.Nics)
            {
                ToolStripMenuItem item = new ToolStripMenuItem(string.Format(FrmMainLocals.NicText, nic.Id, nic.Name, nic.Mac));
                item.Click += new EventHandler(ResetNicItem_Click);
                item.Tag = nic;
                mnuItemResetNicMac.DropDownItems.Add(item);
            }
        }

        /// <summary>
        /// Raised when the nic of a machine has to be reset
        /// </summary>
        /// <param name="sender">The menu item that raised the event</param>
        private void ResetNicItem_Click(object sender, EventArgs e)
        {
            Nic nic = (Nic)((ToolStripMenuItem)sender).Tag;
            nic.ResetMac();
            UpdateMachineDetails(CurrentMachine());
        }

        /// <summary>
        /// Starts the actual scan
        /// </summary>
        private void btnScan_Click(object sender, EventArgs e)
        {
            if (Properties.Settings.Default.SearchFolders == null || Properties.Settings.Default.SearchFolders.Count <= 0)
            {
                MessageBox.Show(this, FrmMainLocals.MsgNoFolders, FrmMainLocals.MsgNoFoldersCaption, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                PerformScan();
            }
        }

        /// <summary>
        /// Show the settings dialog and start the scan afterwards
        /// </summary>
        private void btnManageFolder_Click(object sender, EventArgs e)
        {
            FrmFolders frmFolders = new FrmFolders();
            if (frmFolders.ShowDialog(this) == DialogResult.OK)
            {
                PerformScan();
            }
        }

        /// <summary>
        /// Perform the actual scan
        /// </summary>
        private void FrmMain_Load(object sender, EventArgs e)
        {
            PerformScan();
        }

        private void lstMachines_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            System.Collections.IComparer newComparer = GetComparer(e.Column);

            if (lstMachines.ListViewItemSorter.GetType() == newComparer.GetType())
            {
                // Change the sort direction
                lstMachines.Sorting = lstMachines.Sorting == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
                newComparer = GetComparer(e.Column);
            }
            lstMachines.ListViewItemSorter = newComparer;

            lstMachines.Sort();
        }

        /// <summary>
        /// Returns the comparer for the given column
        /// </summary>
        /// <param name="columnIndex">The index of the column to sort by</param>
        /// <returns>A comparer for sorting the list</returns>
        private System.Collections.IComparer GetComparer(int columnIndex)
        {
            System.Collections.IComparer newComparer = null;

            switch (columnIndex)
            {
                case 1: newComparer = new FilenameComparer(lstMachines.Sorting);
                    break;
                case 2: newComparer = new NicComparer(lstMachines.Sorting);
                    break;
                case 3: newComparer = new RamComparer(lstMachines.Sorting);
                    break;
                case 4: newComparer = new StateComparer(lstMachines.Sorting);
                    break;
                default: newComparer = new NameComparer(lstMachines.Sorting);
                    break;
            }
            return newComparer;
        }

        /// <summary>
        /// Performs the actual scan in background
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The arguments for the background worker</param>
        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (Properties.Settings.Default.SearchFolders == null)
            {
                Properties.Settings settings = Properties.Settings.Default;
                settings.SearchFolders = new System.Collections.Specialized.StringCollection();
                settings.Save();
            }
            string[] folders = new string[Properties.Settings.Default.SearchFolders.Count];
            for (int i = 0; i < folders.Length; i++)
            {
                folders[i] = Properties.Settings.Default.SearchFolders[i];
            }

            if (this.scanner != null)
            {
                this.scanner.Dispose();
                this.scanner = null;
            }

            this.scanner = new FolderScanner(folders, true, true);

            this.scanner.NewMachine += new DlgMachineInformationChanged(scanner_NewMachine);
            this.scanner.ScanningFolder += new DlgScanningFolder(scanner_ScanningFolder);

            this.scanner.ScanForMachines();
        }

        /// <summary>
        /// Perform some finalization
        /// </summary>
        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            UpdateListHighlighting();

            toolStatusLabel.Text = FrmMainLocals.NotScanningText;
        }
    }
}
